package net.qhzw.geologr5.gis.service.system.file2linux;

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Properties;

/***
 * 文件上传/删除/解压缩
 */
@Slf4j
public class SFTPUtil {

    /*
     * @Description: 获取文件上传的安全通道
     * @param session
     * @return
     */
    public static Channel getChannel(Session session) {
        ChannelSftp channel = null;
        try {
            channel = (ChannelSftp) session.openChannel("sftp");
            channel.connect();

            Class<ChannelSftp> c = ChannelSftp.class;
            Field f = c.getDeclaredField("server_version");
            f.setAccessible(true);
            ChannelSftp chSftp = (ChannelSftp) channel;
            f.set(chSftp, 2);
            chSftp.setFilenameEncoding("GBK");

//            System.out.println("获取连接成功");
            log.info("get Channel success!");
        } catch (JSchException | SftpException | NoSuchFieldException | IllegalAccessException e) {
            log.info("get Channel fail!", e);
        }
        return channel;
    }

    /*
     * @Description:获取连接信息，返回session，在session中获取安全通道
     * @param host：连接主机ip
     * @param port:端口号，一般sftp依托于ssh。端口号22
     * @param username：用户名
     * @param password：密码
     * @return
     */
    public static Session getSession(String host, int port, String username, final String password) {
        Session session = null;
        try {
            JSch jsch = new JSch();
            jsch.getSession(username, host, port);
            session = jsch.getSession(username, host, port);
            session.setPassword(password);
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            session.setConfig(sshConfig);
            session.connect();
            log.info("Session connected!");
        } catch (JSchException e) {
            log.info("get Channel failed!", e);
        }
        return session;
    }

    /*
     * @Description:创建文件夹
     * @param sftp
     * @param dir : 创建的文件夹名字
     */
    public static Boolean mkdir(String dir) {
        Session s = getSession(SFTPInfo.SFTP_REQ_HOST, SFTPInfo.SFTP_DEFAULT_PORT, SFTPInfo.SFTP_REQ_USERNAME,
                SFTPInfo.SFTP_REQ_PASSWORD);
        Channel channel = getChannel(s);
        ChannelSftp sftp = (ChannelSftp) channel;
        Boolean result = false;
        try {
            sftp.cd("/");//相当于在linux命令行执行cd / ，然后在打开的目录下创建
            sftp.mkdir(dir);
            log.info("创建文件夹成功");
            result = true;
        } catch (SftpException e) {
            log.error("创建文件夹失败");
            result = false;
            e.printStackTrace();
        }
        return result;
    }


    /***
     * 执行linux 命令
     * @param sshSession
     * @param command
     */
    public static void exec(Session sshSession, String command) {
        Session s = getSession(SFTPInfo.SFTP_REQ_HOST, SFTPInfo.SFTP_DEFAULT_PORT, SFTPInfo.SFTP_REQ_USERNAME,
                SFTPInfo.SFTP_REQ_PASSWORD);
        ChannelExec channelExec = null;
        try {
            log.info("Session connected");
            log.info("Opening Channel");
            Channel channel = s.openChannel("exec");
            channelExec = (ChannelExec) channel;
            channelExec.setCommand(command);
            channelExec.connect();
        } catch (Exception e) {
            e.printStackTrace();
            channelExec = null;
        } finally {
            channelExec.disconnect();
        }
    }


    /*
     * @Description: 文件上传的方法
     * @param sftp : 客户端
     * @param dir : 指定上传文件的目录
     * @param file : 上传的文件
     * @return :
     */
    public static Boolean uploadFile(String dir, File file) {
        Session s = getSession(SFTPInfo.SFTP_REQ_HOST, SFTPInfo.SFTP_DEFAULT_PORT, SFTPInfo.SFTP_REQ_USERNAME,
                SFTPInfo.SFTP_REQ_PASSWORD);
        Channel channel = getChannel(s);
        ChannelSftp sftp = (ChannelSftp) channel;
        Boolean result = false;
        try {
            sftp.cd("/" + dir);
            log.info("打开目录");
            if (file != null) {
                sftp.put(new FileInputStream(file), file.getName());
                result = true;
            } else {
                result = false;
            }
        } catch (Exception e) {
            log.error("上传失败！", e);
            result = false;
        }
        closeAll(sftp, channel, s); // 关闭连接
        return result;
    }

    /**
     * @param directory    下载目录
     * @param downloadFile 下载的文件
     * @param saveFile     存在本地的路径
     * @Description: 文件下载
     */
    public static Boolean download(String directory, String downloadFile, String saveFile) {
        Session s = getSession(SFTPInfo.SFTP_REQ_HOST, SFTPInfo.SFTP_DEFAULT_PORT, SFTPInfo.SFTP_REQ_USERNAME,
                SFTPInfo.SFTP_REQ_PASSWORD);
        Channel channel = getChannel(s);
        ChannelSftp sftp = (ChannelSftp) channel;
        Boolean result = false;
        try {
            sftp.cd("/" + directory);
            sftp.get(downloadFile, saveFile);
            result = true;
        } catch (Exception e) {
            result = false;
            log.error("下载失败！", e);

        }
        return result;
    }

    /**
     * @param directory  要删除文件所在目录
     * @param deleteFile 要删除的文件
     * @Description: 文件删除
     */
    public static Boolean delete(String directory, String deleteFile) {
        Session s = getSession(SFTPInfo.SFTP_REQ_HOST, SFTPInfo.SFTP_DEFAULT_PORT, SFTPInfo.SFTP_REQ_USERNAME,
                SFTPInfo.SFTP_REQ_PASSWORD);
        Channel channel = getChannel(s);
        ChannelSftp sftp = (ChannelSftp) channel;
        Boolean result = false;
        try {
            sftp.cd("/" + directory);
            sftp.rm(deleteFile);
            result = true;
        } catch (Exception e) {
            result = false;
            log.error("删除失败！", e);
        }
        return result;
    }

    /***
     * 判断目录是否存在
     * @param path
     * @return
     */
    public static boolean isExistDir(String path) {
        Session s = getSession(SFTPInfo.SFTP_REQ_HOST, SFTPInfo.SFTP_DEFAULT_PORT, SFTPInfo.SFTP_REQ_USERNAME,
                SFTPInfo.SFTP_REQ_PASSWORD);
        Channel channel = getChannel(s);
        ChannelSftp sftp = (ChannelSftp) channel;
        boolean isExist = false;
        try {
            SftpATTRS sftpATTRS = sftp.lstat(path);
            isExist = true;
            return sftpATTRS.isDir();
        } catch (Exception e) {
            if (e.getMessage().toLowerCase().equals("no such file")) {
                isExist = false;
            }
        }
        return isExist;
    }

    /***
     * 复制远程文件到目标目录
     * @param srcSftpFilePath 远程路径
     * @param distSftpFilePath 目标路径
     * @throws SftpException
     * @throws IOException
     */
    public static boolean copyFile(String srcSftpFilePath, String distSftpFilePath) throws NoSuchFieldException, SftpException, IllegalAccessException {
        Session s = getSession(SFTPInfo.SFTP_REQ_HOST, SFTPInfo.SFTP_DEFAULT_PORT, SFTPInfo.SFTP_REQ_USERNAME,
                SFTPInfo.SFTP_REQ_PASSWORD);
        Channel channel = getChannel(s);

        boolean result = false;
        boolean dirExist;
        boolean fileExist;
        fileExist = isExistDir(srcSftpFilePath);
        dirExist = isExistDir(distSftpFilePath);
        //文件不存在直接反回 false
        if (fileExist) {
            return result;
        }
        if (!dirExist) {
            //1建立目录
            mkdir(distSftpFilePath);
        }
        String command = "mv " + srcSftpFilePath + " " + distSftpFilePath;
        exec(s, command);
        result = true;
        return result;
    }


    /***
     * 关闭连接
     * @param sftp
     * @param channel
     * @param session
     */
    public static void closeAll(ChannelSftp sftp, Channel channel, Session session) {
        try {
            closeChannel(sftp);
            closeChannel(channel);
            closeSession(session);
        } catch (Exception e) {
            log.info("closeAll", e);
        }
    }

    /***
     * 关闭通道
     * @param channel
     */
    private static void closeChannel(Channel channel) {
        if (channel != null) {
            if (channel.isConnected()) {
                channel.disconnect();
            }
        }
    }


    /***
     * 关闭会话
     * @param session
     */
    private static void closeSession(Session session) {
        if (session != null) {
            if (session.isConnected()) {
                session.disconnect();
            }
        }
    }


}
